package com.ww.exercise.coroutine.easy.q2

import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import java.io.IOException

// 统计结果数据类
data class StatResult(
    val type: String, // 任务类型：日活、周活等
    val value: Long // 统计数值
)

// 失败信息数据类
data class FailureInfo(
    val taskName: String,
    val exception: Throwable
)

// 最终返回结果封装
data class TaskResult(
    val successes: List<StatResult>,
    val failures: List<FailureInfo>
)

class StatisticsManager {
    /**
     * ==========================================================================
     * = coroutineScope {}表示：该suspend函数的实现逻辑完全封装在coroutineScope函数创建的协程作用域中，函数的返回值由coroutineScope作用域内的代码计算并返回
     * 核心概念解析：coroutineScope是什么
     * coroutineScope是Kotlin协程库提供的一个suspend函数，它的核心作用是：
     * 1、创建一个新的协程作用域（属于结构化并发的关键API）；
     * 2、等待作用域内所有子协程执行完毕后再结束自身；
     * 3、继承外部协程的上下文（如调度器、异常处理等），单拥有独立的生命周期。
     *
     * coroutineScope就像一个“协程容器”：可以在它的lambda中启动多个子协程（比如用launch或async），
     * 它会“等待”所有子协程做完事情后，再返回结果（lambda最后一行的表达式结果）
     *
     * 关键特性总结
     * 1、结构化并发：coroutineScope确保所有子协程完成后才返回，避免“孤儿协程”；
     * 2、异常传播：若作用域内任何子协程抛出异常，coroutineScope会立即取消所有子协程，并将异常向上传播；
     * 3、非阻塞等待：coroutineScope是suspend函数，等待子协程时不会阻塞线程，它只会挂起当前协程；
     * （coroutineScope比普通launch或async更安全的原因——它强制了协程的生命周期管理，符合Kotlin协程“结构化并发”的设计理念）
     * ==========================================================================
     */
    // 执行所有统计任务
    suspend fun executeAllStats(): TaskResult = coroutineScope {
        // 启动四个并发任务，每个任务都用try-catch隔离异常
        // TODO 函数调用传参写法看看
        val dailyTask = async { safeExecute("日活统计") { calculateDailyActive() } }
        val weeklyTask = async { safeExecute("周活统计") { calculateWeeklyActive() } }
        val monthlyTask = async { safeExecute("月活统计") { calculateMonthlyActive() } }
        val yearlyask = async { safeExecute("年活统计") { calculateYearlyActive() } }

        // 等待所有任务完成并收集数据
        val allResults = listOf(
            dailyTask.await(),
            weeklyTask.await(),
            monthlyTask.await(),
            yearlyask.await()
        )

        // 分离成功和失败的结果
        TaskResult(
            // TODO filterIsInstance函数使用
            /**
             * filterIsInstance<T>()是一个非常实用的集合扩展函数，用于从集合中筛选出指定类型（或其子类型）的元素，
             * 并自动将结果转换为该类型的列表。简化了“筛选特定类型元素”并“进行类型转换”的操作，避免了手动类型判断和强转的繁琐。
             * 1.自动类型转换：筛选后的列表元素类型直接为T，无需手动强转（避免了as关键字的使用）；
             * 2.子类型兼容：如果元素是T的子类型实例，也会被筛选出来（符合面向对象的多态特性）；
             * 3.空安全：如果集合中包含null，null不会被任何非nullable类型T筛选出来。
             */
            successes = allResults.filterIsInstance<StatResult>(),
            failures = allResults.filterIsInstance<FailureInfo>()
        )
    }

    // 执行单个任务，捕获并封装异常
    private suspend fun safeExecute(taskName: String, task: suspend () -> Long): Any {
        return try {
            StatResult(taskName, task())
        } catch (e: Exception) {
            FailureInfo(taskName, e)
        }
    }

    // 以下模拟统计任务实现
    private suspend fun calculateDailyActive(): Long {
        delay(500)
        // 随机抛出异常，模拟任务失败场景
        if (Math.random() < 0.2) {
            throw IOException("日活统计数据读取失效")
        }
        return (Math.random() * 10000).toLong()
    }

    private suspend fun calculateWeeklyActive(): Long {
        delay(800)
        // 随机抛出异常，模拟任务失败场景
        if (Math.random() < 0.15) {
            throw IOException("周活统计数据读取失效")
        }
        return (Math.random() * 50000).toLong()
    }

    private suspend fun calculateMonthlyActive(): Long {
        delay(1000)
        // 随机抛出异常，模拟任务失败场景
        if (Math.random() < 0.25) {
            throw IOException("月活统计数据读取失效")
        }
        return (Math.random() * 200000).toLong()
    }

    private suspend fun calculateYearlyActive(): Long {
        delay(1200)
        // 随机抛出异常，模拟任务失败场景
        if (Math.random() < 0.1) {
            throw IOException("年活统计数据读取失效")
        }
        return (Math.random() * 1000000).toLong()
    }
}

suspend fun main() {
    val manager = StatisticsManager()
    val result = manager.executeAllStats()

    println("==== 统计结果 ====")
    println("成功任务(${result.successes.size}个)：")
    result.successes.forEach { println("${it.type}:${it.value}") }

    println("\n失败任务(${result.failures.size}个)")
    result.failures.forEach {
        println("${it.taskName}失败：:${it.exception.message}")
    }
}